package cn.tedu.reflection;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/*本类用于测试反射*/
public class TestReflect {
    /*单元测试方法格式:public+void+无参+ @Test*/
    //1,通过单元测试方法,获取目标类Student对应的字节码对象
    @Test
    public void getClazz() throws ClassNotFoundException {
        //练习获取字节码对象的3种方式
        Class<?> clazz1 = Class.forName("cn.tedu.reflection.Student");
        Class<?> clazz2 = Student.class;
        Class<?> clazz3 = new Student().getClass();

        //打印获取到的字节码对象
        System.out.println(clazz1);//class cn.tedu.reflection.Student
        System.out.println(clazz2);
        System.out.println(clazz3);
        //通过刚刚获取到的字节码对象,获取Student类的资源
        //获取字节码对象对应的全路径名
        System.out.println(clazz1.getName());//cn.tedu.reflection.Student
        //获取字节码对象对应的类名
        System.out.println(clazz1.getSimpleName());//Student
        //获取Student类对应的包对象
        System.out.println(clazz1.getPackage());//package cn.tedu.reflection
        //先获取Student类对应的包对象,再通过包对象获取包名
        System.out.println(clazz1.getPackage().getName());//cn.tedu.reflection

    }
    //2.通过单元测试方法,获取Student类中的成员变量
    @Test
    public void getFie() throws ClassNotFoundException {
    //获取目标类对应的字节码对象
        Class<?> clazz = Class.forName("cn.tedu.reflection.Student");
        //通过字节码对象获取成员变量们
        Field[] fs = clazz.getFields();
        //打印数组,查看数组中的内容
        System.out.println(Arrays.toString(fs));
        //遍历数组,获取每个成员
        for (Field f:fs){
            /*注意!目前成员变量的修饰符需要是public才能获取到,其他修饰符可以通过暴力反射获取*/
            System.out.println(f.getName());
            System.out.println(f.getType());
        }

    }
    //3,通过单元测试方法,获取Student类中的成员方法
    @Test
    public  void getunction(){
        //获取目标类对应的字节码对象
        Class<?>clazz=Student.class;
        //通过字节码对象获取目标类中的成员方法们
        Method[]ms=clazz.getMethods();
        //打印查看方法数组对象的所有内容
        System.out.println(Arrays.toString(ms));
        //使用高效for循环遍历数组,使用每个具体的方法对象,查看当前方法的具体信息
        for (Method m:ms){
            System.out.println(m.getName());//通过遍历到的方法对象,查看方法名
            System.out.println(Arrays.toString(m.getParameterTypes()));//通过遍历到的方法对象,查看方法的参数类型

        }
    }

    //4,通过单元测试方法,获取目标类Student中的所有构造方法们
    @Test
    public void getCons(){
        //遍历获取到的构造函数们,分别获取每个构造函数方法名与方法参数,打印出来
        Class<?>clazz=Student.class;
        //通过字节码对象,获取目标类中的构造方法们
        Constructor<?>[] cc = clazz.getConstructors();
        //遍历获取到的构造函数们,分别获取每个构造函数方法名和方法参数并打印出来
        System.out.println(Arrays.toString(cc));
        for (Constructor c:cc){
            System.out.println(c.getName());
            System.out.println(Arrays.toString(c.getParameterTypes()));
        }
    }

//5,通过反射,创建目标类对象
    @Test
    public void getObject() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //获取字节码对象
        Class<?> clazz=Student.class;
        //通过放射技术创建目标类对象,注意抛出异常
        /*反射创建对象方案1:通过触发目标类中的无参构造创建对象*/
        Object o = clazz.newInstance();
        System.out.println(o);

        /*反射创建对象方案2:通过触发目标类的全参构造创建对象*/
        /*1,获取指定的构造函数对象,
        注意需要指定构造函数的参数,并且传入的是.class字节码对象*/
        //获取目标类中指定的那个全参构造对象
        Constructor<?> c = clazz.getConstructor(String.class,int.class);
        System.out.println(c);
        //通过获取到的构造函数:创建对象+给对象的所有属性赋值
        Object o2 = c.newInstance("找六", 6);
        System.out.println(o2);

        Constructor<?> c1= clazz.getConstructor(String.class, int.class, int.class);
        System.out.println();
        Object o3 = c1.newInstance("张三", 4, 5);
        System.out.println(o3);

    }



}
